package hit.reports;

import hit.core.HomeInventoryTracker;
import hit.core.Item;
import hit.core.ItemIndex;
import hit.core.Product;
import hit.core.ProductGroup;
import hit.core.ProductIndex;
import hit.core.Size;
import hit.core.StorageUnit;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;

/**
 * Builds the report of notices for the user
 *
 */
public class NoticesReport implements ReportDesigner, Visitor {
	List<ProductGroup> containerOrder;
	Map<ProductGroup, List<Product>> inconsistencies;
	Map<ProductGroup, List<Product>> productsInPG;
	ProductIndex index;
	
	static final Comparator<Product> comp = new Comparator<Product>()
	{
		public int compare(Product p1, Product p2)
		{
			int result = p1.GetDescription().compareTo(p2.GetDescription());
			
			return result;
		}
	};
	
	public NoticesReport(){
		containerOrder = new ArrayList<ProductGroup>();
		inconsistencies = new HashMap<ProductGroup, List<Product>>();
		productsInPG = new HashMap<ProductGroup, List<Product>>();
	}
	
	@Override
	public void VisitHITCore(HomeInventoryTracker core) {
		index = core.GetProductIndex();
	}

	@Override
	public void VisitProductGroup(ProductGroup group) {
		containerOrder.add(group);
		List<Product> products = productsInPG.get(group);
		List<Product> badProducts = new ArrayList<Product>();
		inconsistencies.put(group, badProducts);
		for (Product p : products){
			if (group.GetThreeMonth().GetType().GetGroup() != Size.Unit.UnitGroup.COUNT &&
				group.GetThreeMonth().GetType().GetGroup() != p.GetSize().GetType().GetGroup())
			{
				badProducts.add(p);
				inconsistencies.put(group, badProducts);
			}
		}
	}
	
	@Override
	public void VisitProductGroupPost(ProductGroup pg) {
		List<Product> products = new ArrayList<Product>();
		Iterator<ProductGroup> iter = pg.GetProductGroupIterator();
		while (iter.hasNext()){
			products.addAll(productsInPG.get(iter.next()));
		}
		Iterator<Product> iterP = index.GetByProductContainer(pg);
		if (iterP != null){
			while (iterP.hasNext()){
				products.add(iterP.next());
			}
		}
		
		productsInPG.put(pg, products);
	}
	
	@Override
	public void BuildReport(ReportBuilder builder) {
		
		builder.setTitle("Notices");
		boolean noNotices = true;
		for (ProductGroup pg : inconsistencies.keySet()){
			if (!inconsistencies.get(pg).isEmpty()){
				noNotices = false;
			}
		}
		if (noNotices){
			builder.AddObject(new BuilderParagraph("There are no notices at this time."));
		}
		else{
			builder.AddObject(new BuilderHeader("3-Month Supply Warnings"));

			Iterator<ProductGroup> pgIter = containerOrder.iterator();
			while (pgIter.hasNext())
			{
				ProductGroup productGroup = pgIter.next();
				
				if (!inconsistencies.get(productGroup).isEmpty()){
					String pgInfo = "Product group ";
					pgInfo += productGroup.GetRootContainer().GetName();
					pgInfo += "::";
					pgInfo += productGroup.GetName();
					pgInfo += " has a 3-Month supply (";
					pgInfo += productGroup.GetThreeMonth().toString();
					pgInfo += ") that is inconsistant with the following products:";

					builder.AddObject(new BuilderParagraph(pgInfo));

					List<Product> products = inconsistencies.get(productGroup);
					Collections.sort(products, comp);

					Iterator<Product> pIter = products.iterator();

					while (pIter.hasNext())
					{
						Product product = pIter.next();

						String prodInfo = "- ";
						prodInfo += product.GetDescription();
						prodInfo += " (size: ";
						prodInfo += product.GetSize().toString();
						prodInfo += ")";

						builder.AddObject(new BuilderParagraph(prodInfo));
					}

					builder.AddObject(new BuilderBreak());
				}
			}
		}
	}
	
	@Override
	public void VisitStorageUnit(StorageUnit unit) {/*do nothing*/}
	
	@Override
	public void VisitProduct(Product product) {/*do nothing*/}

	@Override
	public void VisitItemIndex(ItemIndex index) {
		throw new UnsupportedOperationException("Not applicable.");
	}
	
	@Override
	public void VisitProductIndex(ProductIndex index) {
		throw new UnsupportedOperationException("Not applicable.");
	}
	
	@Override
	public void VisitItem(Item item) {
		throw new UnsupportedOperationException("Not applicable.");
	}
}
